package com.itheima.j2cache.aop.processor;

import com.itheima.j2cache.anno.Cache;
import com.itheima.j2cache.anno.CacheEvictor;
import com.itheima.j2cache.model.AnnotationInfo;
import com.itheima.j2cache.utils.CacheKeyBuilder;
import com.itheima.pinda.common.utils.SpringApplicationContextUtils;
import lombok.extern.slf4j.Slf4j;
import net.oschina.j2cache.CacheChannel;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;

/**
 * 抽象缓存注解处理器
 *
 * @author llhuang10
 * @date 2021/10/8 9:25
 */
@Slf4j
public abstract class AbstractCacheAnnotationProcessor {

    /**
     * 这里的抽象类没有使用spring的@Compoent注解将该类注入到容器中, 所以不能使用 @Resource依赖注入
     * 需要换成SpringApplicationContextUtils的方式, 来获取ApplicationContext上下文对象, 来获取Bean
     */

    protected CacheChannel cacheChannel;


    /**
     * 初始化公共属性, 供子类使用
     */
    public AbstractCacheAnnotationProcessor() {
        ApplicationContext applicationContext = SpringApplicationContextUtils.getApplicationContext();
        cacheChannel = applicationContext.getBean(CacheChannel.class);
    }

    /**
     * 获取缓存注解处理器对象
     *
     * @param joinPoint 切点
     * @param cache     cache注解
     * @return CacheAnnotationProcessor
     */
    public static CacheAnnotationProcessor getProcessor(ProceedingJoinPoint joinPoint, Cache cache) {
        // 调用子类的构造器时, 会调用父类的构造器, 这样cacheChannel才能被初始化, 被SpringUtils工具类注入值
        return new CacheAnnotationProcessor(joinPoint, cache);
    }

    /**
     * 获取清理缓存注解处理器对象
     *
     * @param joinPoint    切点
     * @param cacheEvictor cacheEvictor注解
     * @return CacheEvictorAnnotationProcessor
     */
    public static CacheEvictorAnnotationProcessor getProcessor(ProceedingJoinPoint joinPoint, CacheEvictor cacheEvictor) {
        return new CacheEvictorAnnotationProcessor(joinPoint, cacheEvictor);
    }

    /**
     * 抽象方法, 处理缓存逻辑, 具体应该由子类具体实现
     *
     * @param joinPoint 切点
     * @return
     */
    public abstract Object process(ProceedingJoinPoint joinPoint) throws Throwable;

    /**
     * 封装注解信息
     * 返回值的泛型只需要指定 @Cache这个注解的原因, 是因为@CacheEvictor注解内部引用了@Cache注解
     *
     * @param joinPoint 切点
     * @param cache     @Cache注解
     * @return AnnotationInfo<Cache>
     */
    protected AnnotationInfo<Cache> getAnnotationInfo(ProceedingJoinPoint joinPoint, Cache cache) {
        AnnotationInfo<Cache> annotationInfo = new AnnotationInfo<>();
        annotationInfo.setAnnotation(cache);
        annotationInfo.setRegion(cache.region());
        annotationInfo.setKey(this.generateKey(joinPoint, cache));
        return annotationInfo;
    }


    /**
     * 动态解析注解信息, 生成key
     *
     * @param joinPoint 切点
     * @param cache     @Cache注解
     * @return key
     */
    private String generateKey(ProceedingJoinPoint joinPoint, Cache cache) {
        String key = cache.key();   // ab:100

        // "" 空串情况下, 就是key值缺省没有填写
        if ("".equals(key)) {
            // "" 空串, 需要重新设置当前可以为: 目标Controller类名:方法名
            String className = joinPoint.getTarget().getClass().getSimpleName();
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            String methodName = methodSignature.getMethod().getName();
            // 方法名
            key = className + ":" + methodName;
        }
        // 获取目标方法上面的参数列表        joinPoint.getArgs()
        // ab:100
        try {
            key = CacheKeyBuilder.generate(key, cache.params(), joinPoint.getArgs());
        } catch (IllegalAccessException e) {
            log.error("动态生成key失败 , ex: {}", e.getMessage(), e);
        }
        return key;
    }
}
